﻿//------------------------------------------------------------------------------
// <copyright file="RpcClientEventSource.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//------------------------------------------------------------------------------

namespace Microsoft.Internal.GamesTest.Rpc.Client.Telemetry
{
    using System;
    using System.Diagnostics;
    using System.Diagnostics.Tracing;
    using System.Globalization;
    using System.Linq;
    using System.Reflection;
    using System.Runtime.CompilerServices;

    /// <summary>
    /// EventSource for RpcClient.
    /// </summary>
    [EventSource(Name = "RpcClientEventSource")]
    internal class RpcClientEventSource : EventSource
    {
        private static readonly Lazy<RpcClientEventSource> Instance = new Lazy<RpcClientEventSource>(() => new RpcClientEventSource());
        private static readonly Assembly mscorlibAssembly = typeof(object).Assembly;
        private static readonly Assembly systemAssembly = typeof(Uri).Assembly;

        private string sessionId;

        private RpcClientEventSource()
        {
            this.sessionId = Guid.NewGuid().ToString();
        }

        /// <summary>
        /// Gets the singleton instance of EventSource.
        /// </summary>
        public static RpcClientEventSource Logger
        {
            get { return Instance.Value; }
        }

        /// <summary>
        /// Analyzes the callstack to get details of the calling method.
        /// </summary>
        /// <returns>The signature of the calling method.</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "While this could be accomplished through a property, it would feel weird.")]
        [NonEvent]
        [MethodImpl(MethodImplOptions.NoInlining)]
        public static string GetCurrentMethod()
        {
            StackTrace stack = new StackTrace();
            StackFrame frame = stack.GetFrames().Skip(1).FirstOrDefault(x => !x.GetMethod().Attributes.HasFlag(MethodAttributes.Private));

            if (frame != null)
            {
                string paramList = string.Join(",", frame.GetMethod().GetParameters().Select(x => x.Name));

                return string.Format(CultureInfo.CurrentCulture, "{0}.{1}({2})", frame.GetMethod().DeclaringType.Name, frame.GetMethod().Name, paramList);
            }

            return string.Empty;
        }

        /// <summary>
        /// Analyzes the callstack to get details of the calling constructor.
        /// </summary>
        /// <param name="callingConstructor">Whether to get the current or the calling constructor.</param>
        /// <returns>The signature of the calling constructor.</returns>
        /// <remarks>For constructors that call another constructor, we want to get the original constructor used.
        /// For example, this is used to capture whether the constructor with or without GUID was used, because constructors
        /// without GUID call the constructors with GUID. In that case we want to log the constructor which called the
        /// constructor with GUID, which is one call stack frame above. Thus we get frame 2 instead of frame 1.</remarks>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "While this could be accomplished through a property, it would feel weird.")]
        [NonEvent]
        [MethodImpl(MethodImplOptions.NoInlining)]
        public static string GetCurrentConstructor(bool callingConstructor)
        {
            StackTrace stack = new StackTrace();

            // Get name of function that called this function (call stack frame 1), or get name of function that called that one (frame 2).
            StackFrame frame = stack.GetFrame(callingConstructor ? 2 : 1);

            string paramList = string.Join(",", frame.GetMethod().GetParameters().Select(x => x.Name));

            return string.Format(CultureInfo.CurrentCulture, "{0}({1})", frame.GetMethod().DeclaringType.Name, paramList);
        }

        /// <summary>
        /// Logs a Module Loaded Event.
        /// </summary>
        /// <param name="parentApplication">The application which loaded the module.</param>
        /// <param name="userName">The user running the parent application.</param>
        /// <param name="machine">The machine on which the parent application is running.</param>
        /// <param name="version">The version of the RpcClient assemblies.</param>
        [NonEvent]
        public void ModuleLoaded(string parentApplication, string userName, string machine, string version)
        {
            this.ModuleLoaded(parentApplication, userName, machine, version, this.sessionId);
        }

        /// <summary>
        /// Logs an Object Created Event.
        /// </summary>
        /// <param name="constructor">The name of the object that has been created.</param>
        [NonEvent]
        public void ObjectCreated(string constructor)
        {
            this.ObjectCreated(constructor, this.sessionId);
        }

        /// <summary>
        /// Logs a Method Called Event.
        /// </summary>
        /// <param name="method">The name of the method that has been called.</param>
        [NonEvent]
        public void MethodCalled(string method)
        {
            this.MethodCalled(method, this.sessionId);
        }

        /// <summary>
        /// Logs a Archive Constructed Event.
        /// </summary>
        /// <param name="constructor">The specific constructor called.</param>
        /// <param name="archiveGuid">The guid of the constructed archive.</param>
        [NonEvent]
        public void ArchiveConstructed(string constructor, Guid archiveGuid)
        {
            this.ArchiveConstructed(constructor, this.sessionId, archiveGuid.ToString());
        }

        /// <summary>
        /// Logs a connected event.
        /// </summary>
        /// <param name="connectionId">The guid for the connection.</param>
        /// <param name="remoteServerPlatform">The remote server platform.</param>
        /// <param name="remoteServerArchitecture">The remote server architecture.</param>
        [NonEvent]
        public void Connected(Guid connectionId, string remoteServerPlatform, string remoteServerArchitecture)
        {
            this.Connected(this.sessionId, connectionId.ToString(), remoteServerPlatform, remoteServerArchitecture);
        }

        /// <summary>
        /// Logs a disconnected event.
        /// </summary>
        /// <param name="connectionId">The guid for the connection.</param>
        [NonEvent]
        public void Disconnect(Guid connectionId)
        {
            this.Disconnected(this.sessionId, connectionId.ToString());
        }

        /// <summary>
        /// Logs a Get Named Collection event.
        /// </summary>
        /// <param name="archiveGuid">The guid of the archive.</param>
        /// <param name="collectionType">The type of the collection.</param>
        [NonEvent]
        public void GetNamedCollection(Guid archiveGuid, Type collectionType)
        {
            this.GetNamedCollection(this.sessionId, archiveGuid.ToString(), this.GetTypeName(collectionType));
        }

        /// <summary>
        /// Logs a Get Named Value event.
        /// </summary>
        /// <param name="archiveGuid">The guid of the archive.</param>
        /// <param name="valueType">The type of the value.</param>
        [NonEvent]
        public void GetNamedValue(Guid archiveGuid, Type valueType)
        {
            this.GetNamedValue(this.sessionId, archiveGuid.ToString(), this.GetTypeName(valueType));
        }

        /// <summary>
        /// Logs a Invoke Remote Method event.
        /// </summary>
        /// <param name="connectionId">The guid for the connection.</param>
        /// <param name="inputArchiveGuid">The input archive guid.</param>
        /// <param name="methodNameLength">The method's name's length.</param>
        /// <param name="methodParameterCount">The number of parameters in the input archive.</param>
        /// <param name="invokePacketSize">The invocation's packet size.</param>
        [NonEvent]
        public void InvokeRemoteMethod(Guid connectionId, Guid inputArchiveGuid, int methodNameLength, int methodParameterCount, int invokePacketSize)
        {
            this.InvokeRemoteMethod(this.sessionId, connectionId.ToString(), inputArchiveGuid.ToString(), methodNameLength, methodParameterCount, invokePacketSize);
        }

        /// <summary>
        /// Logs an Invoke Remote Method Return event.
        /// </summary>
        /// <param name="connectionId">The guid for the connection.</param>
        /// <param name="outputArchiveGuid">The output archive guid.</param>
        [NonEvent]
        public void InvokeRemoteMethodReturn(Guid connectionId, Guid outputArchiveGuid)
        {
            this.InvokeRemoteMethodReturn(this.sessionId, connectionId.ToString(), outputArchiveGuid.ToString());
        }

        /// <summary>
        /// Logs an Invoke Remote Method Threw Custom Exception event.
        /// </summary>
        /// <param name="connectionId">The guid for the connection.</param>
        /// <param name="customExceptionFoundInFactory">Whether or not the custom exception was found in the factory.</param>
        [NonEvent]
        public void InvokeRemoteMethodThrewCustomException(Guid connectionId, bool customExceptionFoundInFactory)
        {
            this.InvokeRemoteMethodThrewCustomException(this.sessionId, connectionId.ToString(), customExceptionFoundInFactory);
        }

        /// <summary>
        /// Logs an Invoke Remote Method Threw Predefined Exception event.
        /// </summary>
        /// <param name="connectionId">The guid for the connection.</param>
        /// <param name="predefinedExceptionHResult">The hresult of the predefined exception.</param>
        [NonEvent]
        public void InvokeRemoteMethodThrewPredefinedException(Guid connectionId, int predefinedExceptionHResult)
        {
            this.InvokeRemoteMethodThrewPredefinedException(this.sessionId, connectionId.ToString(), predefinedExceptionHResult);
        }

        /// <summary>
        /// Logs a Register Exception Factory event.
        /// </summary>
        [NonEvent]
        public void RegisterExceptionFactory()
        {
            this.RegisterExceptionFactory(this.sessionId);
        }

        /// <summary>
        /// Logs a Set Named Value event.
        /// </summary>
        /// <param name="archiveGuid">The guid of the archive.</param>
        /// <param name="valueType">The type of the value.</param>
        [NonEvent]
        public void SetNamedValue(Guid archiveGuid, Type valueType)
        {
            this.SetNamedValue(this.sessionId, archiveGuid.ToString(), this.GetTypeName(valueType));
        }

        /// <summary>
        /// Logs a Set Named Collection event.
        /// </summary>
        /// <param name="archiveGuid">The guid of the archive.</param>
        /// <param name="collectionType">The type of the collection.</param>
        [NonEvent]
        public void SetNamedCollection(Guid archiveGuid, Type collectionType)
        {
            this.SetNamedCollection(this.sessionId, archiveGuid.ToString(), this.GetTypeName(collectionType));
        }

        /// <summary>
        /// Logs a Subscribe event.
        /// </summary>
        [NonEvent]
        public void Subscribe()
        {
            this.Subscribe(this.sessionId);
        }

        /// <summary>
        /// Logs a Subscription Method invoked event.
        /// </summary>
        /// <param name="connectionId">The guid for the connection.</param>
        /// <param name="outputArchiveGuid">The output archive guid.</param>
        /// <param name="methodNameLength">The methods name's length.</param>
        /// <param name="numberOfSubscriptionsInvoked">The number of subscriptions invoked.</param>
        [NonEvent]
        public void SubscriptionMethodInvoked(Guid connectionId, Guid outputArchiveGuid, int methodNameLength, int numberOfSubscriptionsInvoked)
        {
            this.SubscriptionMethodInvoked(this.sessionId, connectionId.ToString(), outputArchiveGuid.ToString(), methodNameLength, numberOfSubscriptionsInvoked);
        }

        /// <summary>
        /// Logs a Try Connect event.
        /// </summary>
        /// <param name="connectionId">The guid for the connection.</param>
        /// <param name="usingServerGuid">Whether or not connecting using a non-default server guid.</param>
        /// <param name="usingPortNumber">Whether or not connecting using a non-default port number.</param>
        /// <param name="operationTimeout">The operation time-out.</param>
        [NonEvent]
        public void TryConnect(Guid connectionId, bool usingServerGuid, bool usingPortNumber, long operationTimeout)
        {
            this.TryConnect(this.sessionId, connectionId.ToString(), (byte)(usingServerGuid ? 1 : 0), (byte)(usingPortNumber ? 1 : 0), operationTimeout);
        }

        /// <summary>
        /// Logs an Unsubscribe event.
        /// </summary>
        [NonEvent]
        public void Unsubscribe()
        {
            this.Unsubscribe(this.sessionId);
        }

        [Event((int)EventId.ModuleLoaded, Message = "User {1} loaded RpcClient.", Level = EventLevel.Informational)]
        private void ModuleLoaded(string parentApplication, string userName, string machine, string version, string session)
        {
            if (this.IsEnabled())
            {
                this.WriteEvent((int)EventId.ModuleLoaded, parentApplication, userName, machine, version, session);
            }
        }

        [Event((int)EventId.ObjectCreated, Message = "Object created: {0}", Level = EventLevel.Informational)]
        private void ObjectCreated(string constructor, string session)
        {
            if (this.IsEnabled())
            {
                this.WriteEvent((int)EventId.ObjectCreated, constructor, session);
            }
        }

        [Event((int)EventId.MethodCalled, Message = "Method called: {0}.", Level = EventLevel.Informational)]
        private void MethodCalled(string method, string session)
        {
            if (this.IsEnabled())
            {
                this.WriteEvent((int)EventId.MethodCalled, method, session);
            }
        }

        [Event((int)EventId.RpcArchiveConstructed, Message = "Archive constructed: {0}.", Level = EventLevel.Informational)]
        private void ArchiveConstructed(string constructor, string session, string archiveGuid)
        {
            if (this.IsEnabled())
            {
                this.WriteEvent((int)EventId.RpcArchiveConstructed, constructor, session, archiveGuid);
            }
        }

        [Event((int)EventId.Connected, Message = "Connected: {2} {3}.", Level = EventLevel.Informational)]
        private void Connected(string session, string connectionId, string remoteServerPlatform, string remoteServerArchitecture)
        {
            if (this.IsEnabled())
            {
                this.WriteEvent((int)EventId.Connected, session, connectionId, remoteServerPlatform, remoteServerArchitecture);
            }
        }

        [Event((int)EventId.Disconnected, Message = "Disconnected.", Level = EventLevel.Informational)]
        private void Disconnected(string session, string connectionId)
        {
            if (this.IsEnabled())
            {
                this.WriteEvent((int)EventId.Disconnected, session, connectionId);
            }
        }

        [Event((int)EventId.GetNamedCollection, Message = "GetNamedCollection: {2}.", Level = EventLevel.Informational)]
        private void GetNamedCollection(string session, string archiveGuid, string collectionType)
        {
            if (this.IsEnabled())
            {
                this.WriteEvent((int)EventId.GetNamedCollection, session, archiveGuid, collectionType);
            }
        }

        [Event((int)EventId.GetNamedValue, Message = "GetNamedValue: {2}.", Level = EventLevel.Informational)]
        private void GetNamedValue(string session, string archiveGuid, string valueType)
        {
            if (this.IsEnabled())
            {
                this.WriteEvent((int)EventId.GetNamedValue, session, archiveGuid, valueType);
            }
        }

        [Event((int)EventId.InvokeRemoteMethod, Message = "InvokeRemoteMethod.", Level = EventLevel.Informational)]
        private void InvokeRemoteMethod(string session, string connectionId, string inputArchiveGuid, int methodNameLength, int methodParameterCount, int invokePacketSize)
        {
            if (this.IsEnabled())
            {
                this.WriteEvent((int)EventId.InvokeRemoteMethod, session, connectionId, inputArchiveGuid, methodNameLength, methodParameterCount, invokePacketSize);
            }
        }

        [Event((int)EventId.InvokeRemoteMethodReturn, Message = "InvokeRemoteMethod Return.", Level = EventLevel.Informational)]
        private void InvokeRemoteMethodReturn(string session, string connectionId, string outputArchiveGuid)
        {
            if (this.IsEnabled())
            {
                this.WriteEvent((int)EventId.InvokeRemoteMethodReturn, session, connectionId, outputArchiveGuid);
            }
        }

        [Event((int)EventId.InvokeRemoteMethodThrewCustomException, Message = "InvokeRemoteMethod threw custom exception.", Level = EventLevel.Informational)]
        private void InvokeRemoteMethodThrewCustomException(string session, string connectionId, bool customExceptionFoundInFactory)
        {
            if (this.IsEnabled())
            {
                this.WriteEvent((int)EventId.InvokeRemoteMethodThrewCustomException, session, connectionId, customExceptionFoundInFactory);
            }
        }

        [Event((int)EventId.InvokeRemoteMethodThrewPredefinedException, Message = "InvokeRemoteMethod threw predefined exception.", Level = EventLevel.Informational)]
        private void InvokeRemoteMethodThrewPredefinedException(string session, string connectionId, int predefinedExceptionHResult)
        {
            if (this.IsEnabled())
            {
                this.WriteEvent((int)EventId.InvokeRemoteMethodThrewPredefinedException, session, connectionId, predefinedExceptionHResult);
            }
        }

        [Event((int)EventId.RegisterExceptionFactory, Message = "Register Exception Factory.", Level = EventLevel.Informational)]
        private void RegisterExceptionFactory(string session)
        {
            if (this.IsEnabled())
            {
                this.WriteEvent((int)EventId.RegisterExceptionFactory, session);
            }
        }

        [Event((int)EventId.SetNamedValue, Message = "SetNamedValue: {2}.", Level = EventLevel.Informational)]
        private void SetNamedValue(string session, string archiveGuid, string valueType)
        {
            if (this.IsEnabled())
            {
                this.WriteEvent((int)EventId.SetNamedValue, session, archiveGuid, valueType);
            }
        }

        [Event((int)EventId.SetNamedCollection, Message = "SetNamedCollection: {2}.", Level = EventLevel.Informational)]
        private void SetNamedCollection(string session, string archiveGuid, string collectionType)
        {
            if (this.IsEnabled())
            {
                this.WriteEvent((int)EventId.SetNamedCollection, session, archiveGuid, collectionType);
            }
        }

        [Event((int)EventId.Subscribe, Message = "Subscribe.", Level = EventLevel.Informational)]
        private void Subscribe(string session)
        {
            if (this.IsEnabled())
            {
                this.WriteEvent((int)EventId.Subscribe, session);
            }
        }

        [Event((int)EventId.SubscriptionMethodInvoked, Message = "Subscription Method Invoked.", Level = EventLevel.Informational)]
        private void SubscriptionMethodInvoked(string session, string connectionId, string outputArchiveGuid, int methodNameLength, int numberOfSubscriptionsInvoked)
        {
            if (this.IsEnabled())
            {
                this.WriteEvent((int)EventId.SubscriptionMethodInvoked, session, connectionId, outputArchiveGuid, methodNameLength, numberOfSubscriptionsInvoked);
            }
        }

        [Event((int)EventId.TryConnect, Message = "Try connect.", Level = EventLevel.Informational)]
        private void TryConnect(string session, string connectionId, byte usingServerGuid, byte usingPortNumber, long operationTimeout)
        {
            if (this.IsEnabled())
            {
                this.WriteEvent((int)EventId.TryConnect, session, connectionId, usingServerGuid, usingPortNumber, operationTimeout);
            }
        }

        [Event((int)EventId.Unsubscribe, Message = "Unsubscribe.", Level = EventLevel.Informational)]
        private void Unsubscribe(string session)
        {
            if (this.IsEnabled())
            {
                this.WriteEvent((int)EventId.Unsubscribe, session);
            }
        }

        /// <summary>
        /// Gets the name of only built-in types.
        /// </summary>
        /// <param name="type">The type to get the name of.</param>
        /// <returns>The name of the type or string.Empty if it is not a built-in type.</returns>
        [NonEvent]
        private string GetTypeName(Type type)
        {
            // Checking if the type belongs to one of the main assemblies.
            if (type.Assembly == mscorlibAssembly ||
                type.Assembly == systemAssembly)
            {
                // We need to check if this is a generic type and if it is,
                // whether or not the generic parameters are built-int types
                // so we can avoid displaying them.
                if (type.IsGenericType &&
                    type.GenericTypeArguments.Any((t) => t.Assembly != mscorlibAssembly && t.Assembly != systemAssembly))
                {
                    return type.Namespace + '.' + type.Name;
                }
                else
                {
                    return type.ToString();
                }
            }
            else
            {
                // We have to return string.Empty since WriteEvent doesn't allow null strings as parameters.
                return string.Empty;
            }
        }

        [NonEvent]
        private unsafe void WriteEvent(int eventId, string arg1, string arg2, bool arg3)
        {
            fixed (char* string1Bytes = arg1)
            {
                fixed (char* string2Bytes = arg2)
                {
                    EventData* dataDesc = stackalloc EventData[3];

                    dataDesc[0].DataPointer = (IntPtr)string1Bytes;
                    dataDesc[0].Size = (arg1.Length + 1) * sizeof(char);

                    dataDesc[1].DataPointer = (IntPtr)string2Bytes;
                    dataDesc[1].Size = (arg2.Length + 1) * sizeof(char);

                    dataDesc[2].DataPointer = (IntPtr)(&arg3);
                    dataDesc[2].Size = sizeof(bool);

                    this.WriteEventCore(eventId, 3, dataDesc);
                }
            }
        }

        [NonEvent]
        private unsafe void WriteEvent(int eventId, string arg1, string arg2, int arg3)
        {
            fixed (char* string1Bytes = arg1)
            {
                fixed (char* string2Bytes = arg2)
                {
                    EventData* dataDesc = stackalloc EventData[3];

                    dataDesc[0].DataPointer = (IntPtr)string1Bytes;
                    dataDesc[0].Size = (arg1.Length + 1) * sizeof(char);

                    dataDesc[1].DataPointer = (IntPtr)string2Bytes;
                    dataDesc[1].Size = (arg2.Length + 1) * sizeof(char);

                    dataDesc[2].DataPointer = (IntPtr)(&arg3);
                    dataDesc[2].Size = sizeof(int);

                    this.WriteEventCore(eventId, 3, dataDesc);
                }
            }
        }

        [NonEvent]
        private unsafe void WriteEvent(int eventId, string arg1, string arg2, string arg3, string arg4)
        {
            fixed (char* string1Bytes = arg1)
            {
                fixed (char* string2Bytes = arg2)
                {
                    fixed (char* string3Bytes = arg3)
                    {
                        fixed (char* string4Bytes = arg4)
                        {
                            EventData* dataDesc = stackalloc EventData[4];

                            dataDesc[0].DataPointer = (IntPtr)string1Bytes;
                            dataDesc[0].Size = (arg1.Length + 1) * sizeof(char);

                            dataDesc[1].DataPointer = (IntPtr)string2Bytes;
                            dataDesc[1].Size = (arg2.Length + 1) * sizeof(char);

                            dataDesc[2].DataPointer = (IntPtr)string3Bytes;
                            dataDesc[2].Size = (arg3.Length + 1) * sizeof(char);

                            dataDesc[3].DataPointer = (IntPtr)string4Bytes;
                            dataDesc[3].Size = (arg4.Length + 1) * sizeof(char);

                            this.WriteEventCore(eventId, 4, dataDesc);
                        }
                    }
                }
            }
        }

        [NonEvent]
        private unsafe void WriteEvent(int eventId, string arg1, string arg2, byte arg3, byte arg4, long arg5)
        {
            fixed (char* string1Bytes = arg1)
            {
                fixed (char* string2Bytes = arg2)
                {
                    EventData* dataDesc = stackalloc EventData[5];

                    dataDesc[0].DataPointer = (IntPtr)string1Bytes;
                    dataDesc[0].Size = (arg1.Length + 1) * sizeof(char);

                    dataDesc[1].DataPointer = (IntPtr)string2Bytes;
                    dataDesc[1].Size = (arg2.Length + 1) * sizeof(char);

                    dataDesc[2].DataPointer = (IntPtr)(&arg3);
                    dataDesc[2].Size = sizeof(byte);

                    dataDesc[3].DataPointer = (IntPtr)(&arg4);
                    dataDesc[3].Size = sizeof(byte);

                    dataDesc[4].DataPointer = (IntPtr)(&arg5);
                    dataDesc[4].Size = sizeof(long);

                    this.WriteEventCore(eventId, 5, dataDesc);
                }
            }
        }

        [NonEvent]
        private unsafe void WriteEvent(int eventId, string arg1, string arg2, string arg3, int arg4, int arg5)
        {
            fixed (char* string1Bytes = arg1)
            {
                fixed (char* string2Bytes = arg2)
                {
                    fixed (char* string3Bytes = arg3)
                    {
                        EventData* dataDesc = stackalloc EventData[5];

                        dataDesc[0].DataPointer = (IntPtr)string1Bytes;
                        dataDesc[0].Size = (arg1.Length + 1) * sizeof(char);

                        dataDesc[1].DataPointer = (IntPtr)string2Bytes;
                        dataDesc[1].Size = (arg2.Length + 1) * sizeof(char);

                        dataDesc[2].DataPointer = (IntPtr)string3Bytes;
                        dataDesc[2].Size = (arg3.Length + 1) * sizeof(char);

                        dataDesc[3].DataPointer = (IntPtr)(&arg4);
                        dataDesc[3].Size = sizeof(int);

                        dataDesc[4].DataPointer = (IntPtr)(&arg5);
                        dataDesc[4].Size = sizeof(int);

                        this.WriteEventCore(eventId, 5, dataDesc);
                    }
                }
            }
        }

        [NonEvent]
        private unsafe void WriteEvent(int eventId, string arg1, string arg2, string arg3, int arg4, int arg5, int arg6)
        {
            fixed (char* string1Bytes = arg1)
            {
                fixed (char* string2Bytes = arg2)
                {
                    fixed (char* string3Bytes = arg3)
                    {
                        EventData* dataDesc = stackalloc EventData[6];

                        dataDesc[0].DataPointer = (IntPtr)string1Bytes;
                        dataDesc[0].Size = (arg1.Length + 1) * sizeof(char);

                        dataDesc[1].DataPointer = (IntPtr)string2Bytes;
                        dataDesc[1].Size = (arg2.Length + 1) * sizeof(char);

                        dataDesc[2].DataPointer = (IntPtr)string3Bytes;
                        dataDesc[2].Size = (arg3.Length + 1) * sizeof(char);

                        dataDesc[3].DataPointer = (IntPtr)(&arg4);
                        dataDesc[3].Size = sizeof(int);

                        dataDesc[4].DataPointer = (IntPtr)(&arg5);
                        dataDesc[4].Size = sizeof(int);

                        dataDesc[5].DataPointer = (IntPtr)(&arg6);
                        dataDesc[5].Size = sizeof(int);

                        this.WriteEventCore(eventId, 6, dataDesc);
                    }
                }
            }
        }
    }
}
